using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace Deepcode.Flux.Core.Systems.CMS
{
	/// <summary>
	/// ContentObjectData represents the XMLData field of a ContentObject
	/// </summary>
	public class ContentObjectData
	{
		// Protected field storage
		protected string _Key								= "";
		protected List<ContentObjectDataField> _fields		= new List<ContentObjectDataField>();
		protected List<ContentObjectData> _children			= new List<ContentObjectData>();

		// Exposed Properties
		public string Key { get { if( _Key == "" ) return "xml"; return _Key; } set { _Key = value; } }

		#region Constructors
		/// <summary>
		/// Default constructor
		/// </summary>
		public ContentObjectData()
		{
		}
		/// <summary>
		/// Constructor populating key name
		/// </summary>
		/// <param name="key"></param>
		public ContentObjectData(string key)
		{
			_Key = key;
		}
		#endregion
		#region Data Field Manipulations
		/// <summary>
		/// Adds a data field with it's value to the fields list
		/// of this object data.
		/// </summary>
		/// <param name="Key"></param>
		/// <param name="Value"></param>
		/// <returns></returns>
		public ContentObjectDataField Add(string Key, string Value)
		{
			ContentObjectDataField codf = new ContentObjectDataField(Key, Value);
			_fields.Add( codf );
			return codf;
		}
		/// <summary>
		/// Retrieve the data field from the list
		/// </summary>
		/// <param name="Key"></param>
		/// <returns></returns>
		public ContentObjectDataField Get(string Key)
		{
			// Find the field
			foreach( ContentObjectDataField codf in _fields )
				if( codf.FieldName == Key ) return codf;

			// Doesn't exist, create it
			ContentObjectDataField create = new ContentObjectDataField(Key, "");
			_fields.Add( create );
			return create;
		}
		/// <summary>
		/// Gets a field value based on it's key
		/// </summary>
		/// <param name="Key"></param>
		/// <returns></returns>
		public string this[string Key]
		{
			get
			{
				return Get( Key ).FieldValue;
			}
			set
			{
				ContentObjectDataField codf = Get(Key);
				codf.FieldValue = value;
			}
		}
		/// <summary>
		/// Returns all of the fields
		/// </summary>
		/// <returns></returns>
		public ContentObjectDataField [] GetFields()
		{
			return _fields.ToArray();
		}
		/// <summary>
		/// Obtain the value of the field, passing back the default 
		/// if the field does not exist.
		/// </summary>
		/// <param name="Key"></param>
		/// <param name="Default"></param>
		/// <returns></returns>
		public string GetValue(string Key, string Default)
		{
			// Find the field
			foreach (ContentObjectDataField codf in _fields)
				if (codf.FieldName == Key)
					return codf.FieldValue;

			return Default;
		}
		/// <summary>
		/// Get the int value of the field, passing back the
		/// default if the field does not exist or cannot 
		/// be converted.
		/// </summary>
		/// <param name="Key"></param>
		/// <param name="Default"></param>
		/// <returns></returns>
		public int GetValue(string Key, int Default)
		{
			string v = GetValue( Key, "" );
			if( v == "" ) return Default;

			try
			{
				int i = Int32.Parse(v);
				return i;
			}
			catch
			{
				return Default;
			}
		}
		/// <summary>
		/// Get the boolean value of the field, passing back
		/// the default if the field does not exist or cannot be
		/// converted.
		/// </summary>
		/// <param name="Key"></param>
		/// <param name="Default"></param>
		/// <returns></returns>
		public bool GetValue(string Key, bool Default)
		{
			string v = GetValue(Key, "");
			if (v == "") return Default;

			try
			{
				bool b = bool.Parse( v );
				return b;
			}
			catch
			{
				return Default;
			}
		}
		/// <summary>
		/// Get the date/time value of the field, passing back
		/// the default if the field does not exist or cannot be
		/// converted.
		/// </summary>
		/// <param name="Key"></param>
		/// <param name="Default"></param>
		/// <returns></returns>
		public DateTime GetValue(string Key, DateTime Default)
		{
			string v = GetValue(Key, "");
			if (v == "") return Default;

			try
			{
				DateTime dt = DateTime.Parse( v );
				return dt;
			}
			catch
			{
				return Default;
			}
		}
		#endregion
		#region Child Object Data
		/// <summary>
		/// Add a child object to this object
		/// </summary>
		/// <param name="Key"></param>
		/// <returns></returns>
		public ContentObjectData AddChild(string Key)
		{
			ContentObjectData cod = new ContentObjectData(Key);
			_children.Add( cod );
			return cod;
		}
		/// <summary>
		/// Finds and returns the first child set of fields with the specified key
		/// </summary>
		/// <param name="Key"></param>
		/// <returns></returns>
		public ContentObjectData GetChild(string Key)
		{
			// Find the first one
			foreach( ContentObjectData cod in _children )
				if( cod.Key == Key ) return cod;

			// Doesn't exist, so create it
			return AddChild( Key );
		}
		/// <summary>
		/// Finds and returns all child sets with the specified key
		/// </summary>
		/// <param name="Key"></param>
		/// <returns></returns>
		public ContentObjectData[] GetChildren(string Key)
		{
			List<ContentObjectData> l = new List<ContentObjectData>();
			foreach( ContentObjectData cod in _children )
				if( cod.Key == Key ) l.Add( cod );

			return l.ToArray();
		}
		/// <summary>
		/// Get all of the children
		/// </summary>
		/// <returns></returns>
		public ContentObjectData[] GetChildren()
		{
			return _children.ToArray();
		}
		#endregion
		#region Serialise and Deserialise to/from string
		/// <summary>
		/// Serialise fields / children into XML data
		/// eg:
		/// <Data>
		/// 	<CDTitle>Now thats what I call music 7</CDTitle>
		/// 	<Artist>Various</Artist>
		/// 	<Children>
		/// 		<Track>
		/// 			<TrackTitle>Dancing in the Moonlight</TrackTitle>
		/// 			<TrackArtist>Freeloader</TrackArtist>
		/// 		</Track>
		/// 		<Track>
		/// 			<TrackTitle>Lady in Red</TrackTitle>
		/// 			<TrackArtist>Chris Rea</TrackArtist>
		/// 		</Track>
		/// 	</Children>
		/// </Data>
		/// </summary>
		/// <returns></returns>
		public string Serialise()
		{
			XmlDocument doc = new XmlDocument();
			XmlNode x = GetXML(doc);
			return x.OuterXml;
		}
		/// <summary>
		/// Returns the XML representing this object data
		/// </summary>
		/// <returns></returns>
		public XmlNode GetXML( XmlDocument xdoc )
		{
			XmlNode root = 	xdoc.CreateNode(XmlNodeType.Element, this.Key, "");

			foreach (ContentObjectDataField field in _fields)
			{
				XmlNode fieldnode = xdoc.CreateNode(XmlNodeType.Element, field.FieldName, "");
				fieldnode.InnerText = field.FieldValue;
				root.AppendChild( fieldnode );
			}

			if (_children.Count > 0)
			{
				XmlNode childrennode = xdoc.CreateNode(XmlNodeType.Element, "Children", "");
				root.AppendChild( childrennode );
				
				// Loop over the children and add into the childXml
				foreach (ContentObjectData obj in _children)
					childrennode.AppendChild( obj.GetXML(xdoc) );
			}

			return root;
		}
		/// <summary>
		/// Deserialise from the XML data into fields / children
		/// </summary>
		/// <param name="data"></param>
		public void DeSerialise(string data)
		{
			// Clear out the fields ready to load from data
			this._fields = new List<ContentObjectDataField>();
			try
			{
				XmlDocument doc = new XmlDocument();
				doc.LoadXml(data);

				foreach (XmlElement e in doc.DocumentElement.ChildNodes)
				{
					if (e.Name == "Children")
					{
						// Process children
						foreach (XmlElement child in e.ChildNodes)
						{
							ContentObjectData d = this.AddChild( child.Name );
							d.DeSerialise( child.OuterXml );
						}
					}
					else
					{
						// Process field
						Add( e.Name, e.InnerText );
					}
				}
			}
			catch { return; }
		}
		#endregion
		#region Utility functionality
		/// <summary>
		/// Clears the data ready for re-population
		/// </summary>
		public void ClearData()
		{
			_fields.Clear();
			_children.Clear();
		}
		#endregion
	}
}
